package cc.linker.steplink.crm.task;

import cc.linker.steplink.crm.commons.MongoDBConstants;
import cc.linker.steplink.crm.commons.RedisConstants;
import cc.linker.steplink.crm.commons.lock.RedisLock;
import cc.linker.steplink.crm.commons.utils.CalendarUtils;
import cc.linker.steplink.crm.commons.utils.NumberUtils;
import cc.linker.steplink.crm.dao.ArticleDataDao;
import cc.linker.steplink.crm.dao.CustomerBindDao;
import cc.linker.steplink.crm.dao.PersonalDao;
import cc.linker.steplink.crm.domain.ArticleData;
import cc.linker.steplink.crm.domain.CustomerBindRelation;
import cc.linker.steplink.crm.domain.PropagationNode;
import cc.linker.steplink.crm.domain.ShareArticleLog;
import com.alibaba.fastjson.JSON;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 传播链路分析，每日汇总
 * 
 * 需统计
 * 1.昨日被传播的文章（新增） -> 计算昨日分享文章数
 * 2.昨日被传播的人（新增）-> 计算昨日阅读人数
 * 3.昨日被传播的文章（新增）的阅读人数 -> 计算昨日阅读次数
 * 4.从根节点开始所有被传播人以及文章 -> 根据这批人从行为表找出，此文章今日产生的点赞、评论数
 */
@Slf4j
@Component
public class AnalysePropagationLinkTask {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private CustomerBindDao customerBindDao;
    @Autowired
    private ArticleDataDao articleDataDao;
    @Autowired
    private PersonalDao personalDao;

    private final static SimpleDateFormat format = new SimpleDateFormat(CalendarUtils.DAY_FORMAT);

    @Async
    public void execute(String day){
        invoke(day);
    }

//    @Async
//    @RedisLock("AnalysePropagationLinkTask")
//    @Scheduled(cron = "0 0 2 * * *")
//    @Scheduled(initialDelay=1000, fixedDelay=10000)
    public void execute(){
        invoke(null);
    }
    
    public void invoke(String whichDay){
        // 获取运行模式1. START_MODE初始运行 2. DAILY_MODE 日常运行
        String runMode = (String) redisTemplate.opsForHash().get(RedisConstants.CRM_SYS_HASHDADA, RedisConstants.CRM_SYS_HASHKEY);
        if (StringUtils.isEmpty(runMode) || RedisConstants.START_MODE.equals(runMode)) {
            // 初始运行 获取传播链路最早信息
            Aggregation aggregation = Aggregation.newAggregation(
                    Aggregation.group("_class").min("readTime").as("readTime"));
            AggregationResults<BasicDBObject> earliestPropagationNode =
                    mongoTemplate.aggregate(aggregation, MongoDBConstants.PROPAGATION_LINK_COLLECTION, BasicDBObject.class);
            Long time = null;

            for (Iterator iterator = earliestPropagationNode.iterator(); iterator.hasNext(); ) {
                Document obj = (Document)iterator.next();
                time = NumberUtils.getLong(obj.get("readTime"));
//                System.out.println(JSON.toJSONString(obj));
            }
            if (time == null) {// 无数据返回
                redisTemplate.opsForHash().put(RedisConstants.CRM_SYS_HASHDADA, 
                        RedisConstants.CRM_SYS_HASHKEY, RedisConstants.START_MODE);
                return;
            }
            Calendar start = Calendar.getInstance();
            start.setTimeInMillis(time * 1000L);

            Calendar yesterdayCal = CalendarUtils.getYesterdayDefault(true);
            Date yesterdayDate = yesterdayCal.getTime();
            String yesterday = format.format(yesterdayDate);

            // 初始统计从最早时间开始，而后每天统计一次
            while (start.before(yesterdayCal)) {// 昨天之前
                Date dayDate = start.getTime();
                String dayFormat = format.format(dayDate);
                analysePropagationData(dayDate, dayFormat);
                if (yesterday.equals(dayFormat)) {
                    redisTemplate.opsForHash().put(RedisConstants.CRM_SYS_HASHDADA, 
                            RedisConstants.CRM_SYS_HASHKEY, RedisConstants.DAILY_MODE);
                    break;
                }
                start.add(Calendar.DAY_OF_MONTH, 1);
            }
            redisTemplate.opsForHash().put(RedisConstants.CRM_SYS_HASHDADA,
                    RedisConstants.CRM_SYS_HASHKEY, RedisConstants.DAILY_MODE);
        } else if (RedisConstants.DAILY_MODE.equals(runMode)){// 日常运行
            Date yesterdayDate = null;
            String yesterday = null;
            if (StringUtils.isEmpty(whichDay)) {
                yesterdayDate = CalendarUtils.getYesterdayDefault();
                yesterday = format.format(yesterdayDate);
            } else {
                try {
                    yesterdayDate = format.parse(whichDay);
                    yesterday = whichDay;
                } catch (ParseException e) {
                    yesterdayDate = CalendarUtils.getYesterdayDefault();
                    yesterday = format.format(yesterdayDate);
                }
            }
            
            analysePropagationData(yesterdayDate, yesterday);
        } else {
            redisTemplate.opsForHash().put(RedisConstants.CRM_SYS_HASHDADA,
                    RedisConstants.CRM_SYS_HASHKEY, RedisConstants.START_MODE);
            return;
        }
        
    }

    /**
     * 昨日数据分析
     * @param yesterdayDate
     * @param yesterday
     */
    private void analysePropagationData(Date yesterdayDate, String yesterday) {
        // 获取营销员列表
        List<CustomerBindRelation> bindRelations = customerBindDao.findBindRelations();
        if (bindRelations.isEmpty()) {
            return;
        }
        
        // 所有营销员
        Map<String, CustomerBindRelation> openids = bindRelations.stream().filter(
                x -> !CustomerBindRelation.StaffType.INNER_STAFF.getType().equals(x.getStaffType()))
                .collect(Collectors.toMap(
                        CustomerBindRelation::getCustomerId, x -> x));

        List<ShareArticleLog> articleLogs = new ArrayList<>();
        for (CustomerBindRelation bindRelation : bindRelations) {
            // 营销员
            String customerId = bindRelation.getCustomerId();
            String globalOpenid = bindRelation.getGlobalOpenid();
            String token = bindRelation.getToken();
            if (StringUtils.isEmpty(globalOpenid) || StringUtils.isEmpty(token)) {
                return;
            }
            // 加入文章分享的时间
            Date joinTime = bindRelation.getCreateTime();
            if (CustomerBindRelation.StaffType.INNER_STAFF.getType().equals(bindRelation.getStaffType())) {
                continue;
            }

            // 每日数据
            int articleCount = 0;
            int readCount = 0;
            int peopleCount = 0;
            int likeCount = 0;
            int commentCount = 0;
            // 文章数
            Set<String> aids = new HashSet<>();
            // 阅读次数，以文章计
            Map<String, Set<String>> articleRead = new HashMap<>();
            // 阅读次数，以文章计（所有文章，所有人）
            Map<String, Set<String>> articleReadAll = new HashMap<>();
            // 阅读人数，传播范围
            Set<String> uids = new HashSet<>();
            // 点赞数
            // 评论数

//            LOGGER.info("<------营销员传播链路数据WHERE----->globalOpenid:"+globalOpenid+"<-->token:"+token);
            //营销员传播链路数据
            Criteria criteria = Criteria.where("guider").is(globalOpenid).and("oneToken").is(token);
            List<PropagationNode> rootNodes = mongoTemplate.find(Query.query(criteria), PropagationNode.class,
                    MongoDBConstants.PROPAGATION_LINK_COLLECTION);

//            LOGGER.info("<------营销员传播链路数据----->"+JSON.toJSONString(rootNodes));

            if (rootNodes == null || rootNodes.isEmpty()) {
                continue;
            }
            // 找到营销员传播的子节点
            getPropagationNode(rootNodes, openids, yesterday, aids, uids, articleRead, articleReadAll);

            articleCount = aids.size();
            peopleCount = uids.size();
            // 阅读次数
            for (Set<String> readPeople : articleRead.values()) {
                readCount = readCount + readPeople.size();
            }
            // 文章点赞数、评论数 Map<String, Set<String>> articleRead
            for (Map.Entry article : articleReadAll.entrySet()) {
                String aid = (String) article.getKey();
                // 该文章昨日受众用户globalOpenid
                Set<String> readPeople = (Set) article.getValue();
                StringBuffer readPeopleStringBu = new StringBuffer();
                if (readPeople.size() > 0) {
                    // 获取该文章昨日点赞、评论数据
                    for(String readP:readPeople){
                        readPeopleStringBu.append(readP).append(":").append(token).append(",");
                    }
                    String readPeopleString = readPeopleStringBu.substring(0,readPeopleStringBu.length()-1);
                    ArticleData data = articleDataDao.listArticleDataByAidAndType(aid, yesterday, readPeopleString);
                    if (data != null) {
                        likeCount = likeCount + data.getLikeAmount();
                        commentCount = commentCount + data.getCommentAmount();
                    }
                }
            }

            if ((articleCount + readCount + peopleCount + likeCount + commentCount) > 0) {
                ShareArticleLog shareArticleLog = new ShareArticleLog(UUID.randomUUID().toString(),customerId,
                        yesterdayDate, articleCount, readCount, peopleCount, likeCount, commentCount,0);
                articleLogs.add(shareArticleLog);
            }
        }

        // 批量保存
        if (articleLogs != null && !articleLogs.isEmpty()) {
            articleDataDao.insertShareArticleLogBatch(articleLogs);
        }
    }

    private void getPropagationNode(List<PropagationNode> rootNodes, Map<String, CustomerBindRelation> openids,
                                    String yesterday, Set<String> aids, Set<String> uids,
                                    Map<String, Set<String>> articleRead, Map<String, Set<String>> articleReadAll) {
        for (PropagationNode rootNode : rootNodes) {
            // 文章id
            String aid = rootNode.getAid();
            // 传播的globalOpenid
            String goid = rootNode.getGlobal_openid();
            // 阅读时间
            Long readTime = rootNode.getReadTime();
            String day = format.format(new Date(readTime * 1000L));
            String customerId;
            String orgId = customerBindDao.getOrgIdFromWeixinList(rootNode.getOneToken());
            customerId = personalDao.getCustomerIdByWxGlobalOpenIdAndOrgId(rootNode.getGlobal_openid(), orgId);
            if (StringUtils.isEmpty(customerId)) {
                customerId = personalDao.getCustomerIdByOpenIdAndOrgId(rootNode.getGlobal_openid(), orgId);
            }
            if (StringUtils.isEmpty(customerId)) {
                return;
            }
            if (openids.containsKey(customerId)
                    && new Date(readTime * 1000L)
                    .after(openids.get(customerId).getCreateTime())) {
                // 不计入下级营销员的数据
                continue;
            } else {
                if (yesterday.equals(day)) {
                    aids.add(aid);
                    uids.add(goid);
                    if (articleRead.containsKey(aid)) {
                        articleRead.get(aid).add(goid);
                    } else {
                        Set<String> aidSet = new HashSet<>();
                        aidSet.add(goid);
                        articleRead.put(aid, aidSet);
                    }
                }
                if (articleReadAll.containsKey(aid)) {
                    articleReadAll.get(aid).add(goid);
                } else {
                    Set<String> aidSet = new HashSet<>();
                    aidSet.add(goid);
                    articleReadAll.put(aid, aidSet);
                }
                getSubNodes(rootNode, openids, yesterday, aids, uids, articleRead, articleReadAll);
            }
        }
    }

    private void getSubNodes(PropagationNode rootNode, Map<String, CustomerBindRelation> openids, String yesterday,
                             Set<String> aids, Set<String> uids, Map<String, Set<String>> articleRead,
                             Map<String, Set<String>> articleReadAll) {
        List<PropagationNode> subNodes = mongoTemplate.find(
                Query.query(Criteria.where("pid").is(rootNode.getId())), PropagationNode.class,
                MongoDBConstants.PROPAGATION_LINK_COLLECTION);
        if (!subNodes.isEmpty()) {
            //List<PropagationNode> subList = new ArrayList<>();
            for (PropagationNode node : subNodes) {
                // 文章id
                String aid = node.getAid();
                // 传播的globalOpenid
                String goid = node.getGlobal_openid();
                // 阅读时间
                Long readTime = node.getReadTime();
                String day = format.format(new Date(readTime * 1000L));
                if (openids.containsKey(goid)
                        && new Date(readTime * 1000L).after(openids.get(goid).getCreateTime())) {
                    // 不计入下级营销员的数据
                    continue;
                } else {
                    if (yesterday.equals(day)) {
                        aids.add(aid);
                        uids.add(goid);
                        if (articleRead.containsKey(aid)) {
                            articleRead.get(aid).add(goid);
                        } else {
                            Set<String> aidSet = new HashSet<>();
                            aidSet.add(goid);
                            articleRead.put(aid, aidSet);
                        }
                    }
                    if (articleReadAll.containsKey(aid)) {
                        articleReadAll.get(aid).add(goid);
                    } else {
                        Set<String> aidSet = new HashSet<>();
                        aidSet.add(goid);
                        articleReadAll.put(aid, aidSet);
                    }
                    //subList.add(node);
                    getSubNodes(node, openids, yesterday, aids, uids, articleRead, articleReadAll);
                }
            }
            //rootNode.setSubNodes(subList);
        }
    }

}

